<!--
 * @author: wangshuaixue
 * @Date: 2024-10-10 17:25:37
 * @description: 
 * @LastEditTime: 2025-04-11 10:08:51
 * @LastEditors: wangshuaixue
 * @FilePath: /sqlsearch/src/views/sqlsearch/index.vue
-->
<template>
  <div v-loading="loading">
    <div class="search-wrap">
      <div class="content">
        <div class="home-bg" />
        <!-- 请选择要查询的数据库 -->
        <div class="select-sql">
          <img src="@/assets/images/search-title.png" class="title-img" alt="" />
          <div>
            <el-select
              v-model="sqlDbname"
              placeholder="请选择"
              popper-class="select-cls"
              class="select-wrap"
              @change="handleSelect"
            >
              <el-option v-for="item in dblist" :key="item.id" :label="item.dbtitle" :value="item.dbname" />
            </el-select>
          </div>
        </div>
        <div class="top">
          <div v-if="questionList.length > 0">
            <div class="title">
              <img src="@/assets/images/re-like.png" alt="" />
              <span>推荐问题</span>
            </div>
            <div class="recommond-container">
              <div class="recommond-q" v-for="(item, index) in questionList" :key="index" @click="changeqa(item)">
                <div class="q-title">{{ item.question }}</div>
              </div>
              <span v-if="questionList.length == 0">暂无推荐问题</span>
            </div>
          </div>
        </div>
        <div class="item" v-for="(item, index) in contentList" :key="index">
          <!-- 推荐问题模块 分页-->
          <div class="recmd" v-if="item.type == 'recommond'">
            <Recommond :questionList="item.list" @checkQuestion="changeqa" />
          </div>
          <div class="question" v-else-if="item.type == 'user'">
            <div class="question-info">{{ item.content }}</div>
            <img class="avatar" src="../../assets/user.png" />
          </div>
          <div class="result" v-else>
            <img class="avatar" src="../../assets/robot.png" />
            <div class="result-info" v-if="item.type == 'sql'">
              <div v-html="parseContent(item.content)" />
              <div class="btn-wrap">
                <el-button type="success" round class="left" @click="execute(item.content)">执行一下</el-button>
                <el-button class="edit-btn" round @click="dialogEdit(item.content, index)">编辑</el-button>
                <span class="tips">提示：如果sql执行异常可以编辑后重新执行</span>
                <div @click="like(item, index)" class="like">
                  <el-tooltip class="box-item" effect="dark" content="喜欢" placement="top-start">
                    <div>
                      <img src="@/assets/images/like1.png" alt="" v-if="item.useful" />
                      <img src="@/assets/images/like.png" alt="" v-else />
                    </div>
                  </el-tooltip>
                </div>
              </div>
            </div>
            <div class="result-info" v-else-if="item.type == 'text' || item.type == 'error'">
              <span class="error-txt">{{ item.type == "error" ? "执行异常，错误信息为：" : "" }}</span
              >{{ item.content }}
            </div>
            <div class="result-info" v-else>
              <el-table :data="item.content" border style="width: 100%">
                <el-table-column :prop="cit" :label="cit" v-for="(cit, ind) in item.keys" :key="ind" />
              </el-table>
            </div>
          </div>
        </div>
        <div class="end-check" v-if="contentList.length > 0 && !msgloading">
          <div class="end-check-con">
            <span class="name">切换数据库：</span>
            <el-select
              v-model="sqlDbname"
              placeholder="请选择数据库"
              popper-class="select-cls"
              class="select-wrap"
              @change="handleSelect('end')"
            >
              <el-option v-for="item in dblist" :key="item.id" :label="item.dbtitle" :value="item.dbname" />
            </el-select>
          </div>
        </div>
        <div class="end-recommond" v-if="checkDb">
          <Recommond :questionList="questionList" @checkQuestion="changeqa" />
        </div>

        <!-- loading -->
        <div class="result item" v-if="msgloading">
          <img class="avatar" src="../../assets/robot.png" />
          <div class="result-info loading-info">
            思考中
            <img class="load" src="../../assets/loading.gif" />
          </div>
        </div>
      </div>
    </div>
    <div class="fix-bar">
      <div class="fix-bar-item ipt-wrap" @submit.prevent>
        <el-input
          ref="inputRef"
          :autosize="{ minRows: 2, maxRows: 5 }"
          type="textarea"
          v-model="inputQa"
          placeholder="请输入问题"
          class="hide-scrollbar"
          @keydown.enter="send"
        />
        <el-tooltip class="box-item" effect="dark" content="发送" placement="top">
          <div class="send-btn" @click="sendBtn">
            <img :class="['send-icon', { highLight: inputHighLight }]" src="@/assets/images/send.png" />
          </div>
        </el-tooltip>

        <el-dialog v-model="dialogVisible" title="编辑" width="800">
          <div>
            <sql-code-edit
              :value="sqlCode.content"
              :editor-placeholder="'请输入sql语句进行查询'"
              :tab-size="4"
              @change="changeSqlCode"
            />
          </div>
          <template #footer>
            <div class="dialog-footer">
              <el-button @click="dialogClose">取消</el-button>
              <el-button type="primary" @click="dialogSave"> 确认修改 </el-button>
            </div>
          </template>
        </el-dialog>
      </div>
      <div class="fix-bar-item rules-wrap">
        <el-tooltip
          content="<div>1、在SQL语句输入框中输入您想要执行的SQL查询语句；</div>
<div>2、点击“执行”按钮。 如果SQL语句正确，页面会显示查询结果；</div>
<div>3、如果SQL语句错误，根据错误提示修改SQL语句后重新执行。</div>"
          placement="top"
          raw-content
        >
          <div class="rule">
            <SvgIcon name="question-icon" />
            <span>使用说明</span>
          </div>
        </el-tooltip>
      </div>
    </div>
  </div>
</template>

<script setup>
import Prism from "prismjs"
import "prismjs/plugins/copy-to-clipboard/prism-copy-to-clipboard"
import "prismjs/plugins/line-numbers/prism-line-numbers"
import "prismjs/themes/prism.css" // 引入样式
import { ref, onMounted, nextTick, watchEffect } from "vue"
import { ElMessage } from "element-plus"
import { simpleDialog, executeSql, getQaList, getDbNameList } from "@/api/sql"
import { selApprove } from "@/api/konwledge"
import SqlCodeEdit from "./sqlEdit.vue"
import Recommond from "./recommond.vue"

const inputHighLight = ref(false)
const inputRef = ref(null)

// 推荐问题当前推荐
const qaPage = ref(2)
// 无更多推荐内容
const qaEnd = ref(false)
// sql查询中
const msgloading = ref(false)

// 选择问题
const changeqa = (val) => {
  inputQa.value = val.question
  nextTick(() => {
    // 手动触发input事件
    console.log(inputRef.value, "inputRef.value")
    const inputEl = inputRef.value?.input
    console.log(inputEl)
    // inputEl.dispatchEvent(new Event("input", { bubbles: true }))
  })
}
const loading = ref(false)
const sqlCode = ref({
  content: "",
  index: 0
})
const like = async (para, index) => {
  if (para?.useful) return
  const { qid, question, content } = para
  const { code } = await selApprove({ qid, question, answer: content, dbname: sqlDbname.value })
  if (code === 200) {
    // 变颜色
    const current = contentList.value[index]
    current.useful = true
    contentList.value[index] = current
  }
}
//实时获取sql语句
const changeSqlCode = (val) => {
  sqlCode.value.content = val
}
// select 下拉
const dblist = ref([])
const sqlDbname = ref("cms")

const dialogVisible = ref(false)
const inputQa = ref("")
const questionList = ref([])
const contentList = ref([])
// 执行一下sql
async function execute(para) {
  const { code, data } = await executeSql({
    text: para,
    dbname: sqlDbname.value
  })
  if (code === 200) {
    // 区分类型 要展示错误信息
    if (data.type == "error") {
      contentList.value.push({
        type: "error",
        content: data.msg
      })
    } else {
      // type == 'ok'
      // 处理表头 数据表是data.result
      const keys = data.result?.length > 0 ? Object.keys(data?.result[0]) : []
      console.log(keys, "keys")
      contentList.value.push({
        type: "table",
        content: data.result,
        keys
      })
      getQaData()
    }
  } else {
    console.log("error")
  }
}
// 转为高亮代码展示
function parseContent(content) {
  // 暂时拼接开头和结尾
  const joincontent = "```sql\n" + content + "\n```"
  const codeBlockRegex = /(```[\s\S]*?```)/g
  const inlineCodeRegex = /`([^`]+)`/g

  const result = joincontent
    .replace(codeBlockRegex, (match) => {
      const parts = match.split("\n")
      const lang = parts[0].slice(3) // 获取语言标识
      const code = parts.slice(1, -1).join("\n") // 获取代码内容
      return `<pre class="language-${lang}"><code class="language-${lang}">${Prism.highlight(code, Prism.languages[lang], lang)}</code></pre>`
    })
    .replace(inlineCodeRegex, (match, code) => {
      return `<code>${code}</code>`
    })

  return result
}
// 第一屏推荐问题
async function getInitQaData() {
  const { code, data } = await getQaList({
    dbname: sqlDbname.value,
    page: 1,
    pageSize: 4
  })
  if (code === 200) {
    questionList.value = data.resultList
    loading.value = false
    qaEnd.value = data.resultList.length == 4 ? false : true
  }
}
// 获取推荐数据
async function getQaData() {
  // 推荐问题结束
  if (qaEnd.value) return
  const { code, data } = await getQaList({
    dbname: sqlDbname.value,
    page: qaPage.value,
    pageSize: 4
  })
  if (code === 200) {
    // 参数type1放入页面 否则是页面上部的推荐问题
    if (data.resultList.length == 4) {
      qaPage.value++
    } else {
      qaEnd.value = true
    }
    if (data.resultList.length > 0) {
      contentList.value.push({
        type: "recommond",
        list: data.resultList
      })
    }
  }
}
// 回车发送
function send(event) {
  if (event.key === "Enter" && !event.shiftKey) {
    event.preventDefault() // 阻止默认换行行为
    sendmsg()
  }
}
// 点击发送按钮
const sendBtn = () => {
  sendmsg()
}
async function sendmsg() {
  //如果是true重置值，默认不展示底部推荐问题
  checkDb.value ? (checkDb.value = false) : ""
  const txt = inputQa.value.trimEnd()
  if (!txt) return

  // 页面添加一段本人发出去的消息
  contentList.value.push({
    type: "user",
    content: txt
  })

  inputQa.value = ""
  scrollToBottom()

  msgloading.value = true
  const { code, data } = await simpleDialog({
    text: txt,
    dbname: sqlDbname.value
  })
  if (code === 200) {
    contentList.value = contentList.value.concat(data)
    // 分页获取推荐问题
    setTimeout(() => {
      scrollToBottom()
      ElMessage.success("已更新")
      msgloading.value = false
    }, 100)
  }
}

// 更新到底部
function scrollToBottom() {
  setTimeout(() => {
    const myContent = document.getElementsByClassName("search-wrap")[0]
    console.log(myContent, "myContent")
    myContent.scrollTop = myContent.scrollHeight - myContent.clientHeight + 60
  }, 100)
}

function dialogEdit(txt, index) {
  sqlCode.value = {
    content: txt,
    index: index
  }
  setTimeout(() => {
    dialogVisible.value = true
  }, 100)
}
function dialogClose() {
  dialogVisible.value = false
}
function dialogSave() {
  contentList.value[sqlCode.value.index].content = sqlCode.value.content
  console.log(sqlCode.value, "txt")
  dialogVisible.value = false
}
async function getdblist() {
  const { code, data } = await getDbNameList()
  if (code === 200) {
    dblist.value = data
  }
}

// 是否切换底部数据库
const checkDb = ref(false)
function handleSelect(type) {
  qaPage.value = 2
  // 当前选中数据库名称
  getInitQaData()
  // 底部切换数据库时
  if (type == "end") {
    checkDb.value = true
  }
}

onMounted(() => {
  loading.value = true
  Prism.highlightAll()
  getInitQaData()
  getdblist()
})

watchEffect(() => {
  inputHighLight.value = inputQa.value.trimEnd() !== ""
})
</script>

<style lang="scss" scoped>
/* 设置表头背景色 */
::v-deep(.el-table__header-wrapper th) {
  background-color: #f5f5f5; /* 背景色 */
  color: #333; /* 文字颜色 */
  font-weight: bold; /* 加粗字体 */
}
:deep(.el-select__wrapper) {
  color: #222;
  font-weight: bold;
  padding: 17px 12px !important;
  max-height: 48px !important;
  border-radius: 8px;
}
:deep(.el-textarea__inner:focus) {
  border: 0px solid transparent !important;
}
/* 添加一个自定义的类来隐藏滚动条 */
.hide-scrollbar ::-webkit-scrollbar {
  display: none; /* 对于WebKit浏览器 */
}

.hide-scrollbar {
  -ms-overflow-style: none; /* 对于IE和Edge */
  scrollbar-width: none; /* 对于Firefox */
}
:deep(.el-textarea__inner) {
  font-size: 16px;
  box-shadow: none !important;
  border-radius: 14px;
  resize: none;
  min-height: 41px !important;
  padding: 15px 100px 15px 15px;
}
.btn-wrap {
  height: 40px;
  display: flex;
  align-items: center;
  .tips {
    font-size: 13px;
    margin-left: 30px;
  }
  .like {
    height: 40px;
    display: flex;
    align-items: center;
    margin: 20px;
    font-size: 13px;
    vertical-align: middle;
    img {
      margin-left: 5px;
      width: 20px;
    }
  }
  .edit-btn {
    border-color: #0c6bed;
    color: #0c6bed;
  }
}

.title {
  font-size: 18px;
  font-weight: bold;
  margin-bottom: 20px;
  display: flex;
  align-items: center;
  img {
    width: 26px;
    height: 26px;
    margin-right: 6px;
  }
}
.recommond-container {
  width: 100%;
  flex-wrap: wrap;
  display: flex;
  align-items: center;

  .recommond-q {
    color: #444;
    cursor: pointer;
    display: flex;
    justify-content: space-between;
    align-items: center;
    // height: 70px;
    padding: 17px 16px;
    margin: 0 20px 20px 0;
    // background-color: #fff;
    background: rgba(255, 255, 255, 0.5);
    border-radius: 8px;
    border: 1px solid #ffffff;
    &:hover {
      color: #0c6bed;
      background: #ffffff;
      box-shadow: 0px 2px 8px 0px rgba(0, 15, 41, 0.1);
    }
    &:active {
      color: #444444;
      box-shadow: 0px 2px 8px 0px rgba(0, 15, 41, 0.1);
    }
  }
}
.end-check {
  width: 1100px;
  margin: auto;
  padding-top: 24px;
  display: flex;
  justify-content: center;
  .end-check-con {
    font-size: 14px;
    width: 350px;
    display: flex;
    align-items: center;
    .name {
      font-size: 16px;
      color: #666;
    }
  }
}
.end-recommond {
  width: 1100px;
  margin: 20px auto 0;
}
::v-deep(.end-check-con .el-select) {
  width: 248px !important;
}
::v-deep(.end-check-con .el-select__wrapper) {
  padding: 13px 16px !important;
  background-color: #dae2f4;
  border: 1px solid #fff;
  color: #222;
}
::v-deep(.el-select__placeholder) {
  color: #222;
  font-size: 16px;
  font-weight: 400;
}
::v-deep(.end-check-con .el-select__wrapper.is-focused) {
  // box-shadow: none !important;
  border: 1px solid transparent !important;
}

.search-wrap {
  // max-height: 99vh;
  height: calc(100vh - 80px);
  overflow-y: scroll;
  position: relative;
  z-index: 1; /* 确保内容在背景上面 */
  padding-bottom: 200px;
  .content {
    position: relative;
    .home-bg {
      width: 100%;
      height: calc(100vh - 85px);
      position: absolute;
      top: 0;
      left: 0;
      background-image: url(../../assets/images/search-bg.png);
      background-size: 100% 100%;
      z-index: -1; /* 确保背景在内容后面 */
    }
  }
}
.top {
  width: 1100px;
  margin: auto;
  padding-top: 60px;
  display: flex;
}
.select-sql {
  width: 400px;
  text-align: center;
  padding: 170px 0 10px;
  margin: 0 auto;
  .title-img {
    width: 300px;
    height: 28px;
    margin-bottom: 24px;
  }
  .select-wrap {
    width: 400px;
  }
}
.item {
  width: 1100px;
  margin: 0 auto;
}

.fix-bar {
  margin: 0 auto;
  width: 1100px;
  position: fixed;
  bottom: 0;
  left: 200px;
  right: 0; /* 左右边缘贴齐屏幕 */
  margin: 0 auto; /* 水平居中 */
  padding-top: 10px;
  box-sizing: border-box;
  background-color: transparent;
  z-index: 999;
  .ipt-wrap {
    background-color: #fff;
    display: flex;
    justify-content: space-between;
    align-items: center;
    position: relative;
    background: linear-gradient(to right, #5e80ff, #8943db);
    padding: 2px; /* 边框宽度 */
    border-radius: 16px; /* 如果需要圆角 */
  }
  .send-btn {
    font-weight: bold;
    cursor: pointer;
    position: absolute;
    right: 10px;
    color: #409eff;
    display: flex;
    width: 50px;
    align-items: center;
    flex-direction: column;
    .send-icon {
      margin-top: 15px;
      width: 32px;
      height: 32px;
      background: linear-gradient(180deg, #48beff 0%, #2558f8 100%);
      opacity: 0.5;
      border-radius: 50%;
      &:hover {
        opacity: 1;
      }
      &.highLight {
        opacity: 1;
      }
    }
  }
  .rules-wrap {
    padding: 12px 0 20px;
    background-color: #f2f3f7;
    .rule {
      cursor: pointer;
      height: 32px;
      width: 106px;
      display: flex;
      align-items: center;
      justify-content: center;
      color: #666666;
      font-size: 16px;
      line-height: 20px;
      background: rgba(255, 255, 255, 0.5);
      border-radius: 16px;
      border: 1px solid #dddddd;
      span {
        margin-left: 5px;
        font-size: 14px;
      }
      &:hover {
        background-color: #fff;
      }
    }
  }
}

.result,
.question {
  display: flex;
  margin-bottom: 20px;
}
.question {
  justify-content: end;
}

.result-info,
.question-info {
  padding: 16px;
  border-radius: 8px;
  font-size: 16px;
  white-space: break-spaces;
}
.result-info {
  width: 900px;
  background-color: #fff;
}
.question-info {
  color: #222222;
  background-color: #dce4ff;
}
.loading-info {
  width: 400px;
  display: flex;
  align-items: center;
  .load {
    width: 24px;
    height: 24px;
    margin-left: 20px;
  }
}
.error-txt {
  color: red;
}
.avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  margin-right: 10px;
  background-color: #fff;
}
.question {
  .question-info {
    margin-left: 40px;
  }
  .avatar {
    margin-left: 10px;
  }
}
</style>
